import { Injectable } from "@nestjs/common";
import { AccessTokenEntity } from "../entity/access-token.entity";
import { JwtService } from "@nestjs/jwt";
import * as dayjs from 'dayjs';
import { UserEntity } from "../entity/user.entity";
import { defaultUserConfig } from "../helper";
import { JwtPayload } from "jsonwebtoken";
import { v4 as uuid } from 'uuid';
import { RefreshTokenEntity } from "../entity/refresh-token.entity";
import jwt from 'jsonwebtoken';
import { FastifyReply as Response } from 'fastify';
import { DataSource } from "typeorm";


@Injectable()
export class TokenService {
    constructor(
        protected jwtService: JwtService,
        protected dataSource: DataSource
    ) { }
    async refreshToken(accessToken: AccessTokenEntity, response: Response) {
        const { user, refreshToken } = accessToken
        if (refreshToken) {
            //获得一个现在时间
            const now = dayjs(new Date())
            //判断refreshToken是否过期
            if (now.isAfter(refreshToken.expired_at)) {
                return null
            }
            //如果没过期 则生成 新的 access_token和refresh_token
            const token = await this.generateAccessToken(user, now)
            await accessToken.remove()
            response.header('token', token.accessToken.value)

            return token
        }
    }

    async generateAccessToken(user: UserEntity, now: dayjs.Dayjs) {
        let config = defaultUserConfig()
        const accessTokenPayload: JwtPayload = {
            sub: user.id,
            iat: now.unix()
        }

        const signed = this.jwtService.sign(accessTokenPayload)
        const accessToken = new AccessTokenEntity()
        accessToken.value = signed
        accessToken.user = user
        accessToken.expired_at = now.add(config.jwt.token_expired, 'second').toDate()
        await accessToken.save()
        const refreshToken = await this.generateRefreshToken(accessToken, await dayjs(new Date()))

        return { accessToken, refreshToken };

    }

    async generateRefreshToken(accessToken: AccessTokenEntity, now: dayjs.Dayjs) {
        let config = defaultUserConfig()
        const refreshTokenPayload = {
            uuid: uuid()
        }

        const refreshToken = new RefreshTokenEntity()
        refreshToken.value = jwt.sign(refreshTokenPayload, config.jwt.refresh_secret)
        refreshToken.expired_at = now.add(config.jwt.refresh_token_expired, 'second').toDate()
        refreshToken.accessToken = accessToken
        await refreshToken.save()
        return refreshToken
    }
    //检查checkToken是否存在
    async checkAccessToken(value: string) {
        const qb = this.dataSource.getRepository(AccessTokenEntity).createQueryBuilder('access_token');
    return   await qb
            .where(value)
            .leftJoinAndSelect('access_token.user', 'user')
            .leftJoinAndSelect('user.refreshToken', 'refreshToken')
            .getOne();

    }

    /* 
     移除 AccessToken 且 自动移除关联的RefreshToken
    */

    async removeAccessToken(value: string) {
        const result = await this.dataSource.getRepository(AccessTokenEntity).createQueryBuilder('access_token').where(value).getOne()

        if (result) await result.remove()

    }

    /* 验证token是否正确 */
    async verifyAccessToken(token:AccessTokenEntity){
        const config=defaultUserConfig()
        const result=jwt.verify(token.value,config.jwt.secret)
        if (!result) return false;
        return token.user;
    }

}